package com.zzyl.nursing.service.impl;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.zzyl.common.core.domain.model.LoginUser;
import com.zzyl.common.exception.base.BaseException;
import com.zzyl.common.utils.StringUtils;
import com.zzyl.common.utils.UserThreadLocal;
import com.zzyl.framework.web.service.TokenService;
import com.zzyl.nursing.domain.DeviceData;
import com.zzyl.nursing.domain.Elder;
import com.zzyl.nursing.domain.FamilyMemberElder;
import com.zzyl.nursing.dto.DeviceDto;
import com.zzyl.nursing.dto.FamilyDTO;
import com.zzyl.nursing.dto.UserLoginRequestDto;
import com.zzyl.nursing.mapper.FamilyMemberElderMapper;
import com.zzyl.nursing.service.*;
import com.zzyl.nursing.vo.*;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import com.zzyl.nursing.mapper.FamilyMemberMapper;
import com.zzyl.nursing.domain.FamilyMember;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;

/**
 * 老人家属Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-07-31
 */
@Service
@Slf4j
public class FamilyMemberServiceImpl extends ServiceImpl<FamilyMemberMapper, FamilyMember> implements IFamilyMemberService
{
    @Autowired
    private FamilyMemberMapper familyMemberMapper;
    @Autowired
    private WeChatService weChatService;
    @Autowired
    private TokenService tokenService;
    @Autowired
    private IElderService elderService;
    @Autowired
    private FamilyMemberElderMapper familyMemberElderMapper;
    @Autowired
    private IDeviceDataService deviceDataService;
    static List<String> DEFAULT_NICKNAME_PREFIX = ListUtil.of(
            "生活更美好",
            "大桔大利",
            "日富一日",
            "好柿开花",
            "柿柿如意",
            "一椰暴富",
            "大柚所为",
            "杨梅吐气",
            "天生荔枝"
    );

    /**
     * 查询老人家属
     * 
     * @param id 老人家属主键
     * @return 老人家属
     */
    @Override
    public FamilyMember selectFamilyMemberById(Long id)
    {
        return getById(id);
    }

    /**
     * 查询老人家属列表
     * 
     * @param familyMember 老人家属
     * @return 老人家属
     */
    @Override
    public List<FamilyMember> selectFamilyMemberList(FamilyMember familyMember)
    {
        return familyMemberMapper.selectFamilyMemberList(familyMember);
    }

    /**
     * 新增老人家属
     * 
     * @param dto 老人家属
     * @return 结果
     */
    @Override
    public void insertFamilyMember(FamilyDTO dto){
            log.info("开始执行 insertFamilyMember，参数：{}", dto);
            //分析新增老人业务
        /*
        根据要求:
        1,创建新表,对应客户老人家属表,来关联老人家属和客户
        2,此处绑定业务中,是绑定已经在数据库中的老人(家人姓名和身份证号必须是养老院入住的老人)
         */
            //1,根据传入的身份证号idCard和name对老人表进行查询,如果没有数据则抛出异常(未查询到相关老人)
            Elder elder = elderService.getOne(Wrappers.<Elder>lambdaQuery()
                    .eq(Elder::getIdCardNo, dto.getIdCard())
                    .eq(Elder::getName, dto.getName()));
            if (ObjectUtil.isEmpty(elder)) {
                throw new BaseException("未查询到相关老人");
            }
            //拿到当前线程操作者的id
            Long userId = UserThreadLocal.getUserId();
            //封装数据库存储对象
            FamilyMemberElder familyMemberElder = FamilyMemberElder.builder()
                    .elderId(elder.getId())
                    .familyMemberId(userId)
                    .remark(dto.getRemark())
                    .build();
            //调用mapper层存储中间表数据
            familyMemberElderMapper.insert(familyMemberElder);
    }



    /**
     * 修改老人家属
     * 
     * @param familyMember 老人家属
     * @return 结果
     */
    @Override
    public int updateFamilyMember(FamilyMember familyMember)
    {
        return updateById(familyMember) ? 1 : 0;
    }

    /**
     * 批量删除老人家属
     * 
     * @param ids 需要删除的老人家属主键
     * @return 结果
     */
    @Override
    public int deleteFamilyMemberByIds(Long[] ids)
    {
        return removeByIds(Arrays.asList(ids)) ? 1 : 0;
    }

    /**
     * 删除老人家属信息
     * 
     * @param id 老人家属主键
     * @return 结果
     */
    @Override
    public int deleteFamilyMemberById(Long id)
    {
        return removeById(id) ? 1 : 0;
    }

    @Override
    public LoginVo login(UserLoginRequestDto dto) {
        //获得openid
        String openId = weChatService.getOpenId(dto.getCode());
        //查询数据库,得到对应的对象
        FamilyMember familyMember = this.getOne(Wrappers.<FamilyMember>lambdaQuery().eq(FamilyMember::getOpenId, openId));
        //判断当前返回的对象是否为空
        if(ObjectUtils.isEmpty(familyMember)){
            //为空则创建对象,将openid封装
            familyMember = FamilyMember.builder().openId(openId).build();
        }
        //获取当前手机号
        String phoneNumber = weChatService.getPhoneNumber(dto.getPhoneCode());
        //判断当前手机号与数据库中的手机号是否一致,来进行新增和修改的判断
        saveOrUpdateFamilyMember(familyMember,phoneNumber);
        //生成token,并返回对应的对象格式
        Map<String, Object> params=new HashMap<>();
        params.put("userId",familyMember.getId());
        params.put("nickName",familyMember.getName());
        //LoginUser loginUser = new LoginUser();
        //loginUser.setUserId(familyMember.getId());
        String token = tokenService.createToken(params);
        LoginVo loginVo = LoginVo.builder()
                .nickName(familyMember.getName())
                .token(token)
                .build();
        return loginVo;
    }


    private void saveOrUpdateFamilyMember(FamilyMember familyMember, String phoneNumber) {
        if(ObjectUtil.notEqual(familyMember.getPhone(),phoneNumber)){
            //当前手机号不一致,修改手机号
            familyMember.setPhone(phoneNumber);
            this.updateFamilyMember(familyMember);
        }
        //对当前对象的id进行判断,有id则修改,无id则新增
        if(ObjectUtil.isEmpty(familyMember.getId())){
            //新增
            //设置昵称
            String nickName = DEFAULT_NICKNAME_PREFIX.get((int) (Math.random() * DEFAULT_NICKNAME_PREFIX.size()))
                    + StringUtils.substring(familyMember.getPhone(), 7);
            familyMember.setName(nickName);
            save(familyMember);
        }
        //修改
        updateById(familyMember);
    }

    /**
     * 查询家人列表
     * @return
     */
    @Override
    public List<FamilyMemberElderVO> searchFamilyMember() {
        //从线程中拿到当前用户的id,根据当前用户的id查询中间表,根据中间表中记录的老人id,查询对应的老人信息
        Long userId = UserThreadLocal.getUserId();
        List<FamilyMemberElderVO> list = familyMemberMapper.searchFamilyMember(userId);
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        return list;
    }


    /**
     * 分页查询老人及设备信息
     * @return
     */
    @Override
    public List<FamilyMemberElderAndDeviceVO> listByPage() {
        //根据当前线程存储的userId查询老人家属中间表,根据老人id查询老人表,room房间表以及device设备表
        Long userId = UserThreadLocal.getUserId();
        List<FamilyMemberElderAndDeviceVO> list = familyMemberMapper.listByPage(userId);
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        return list;
    }

    /**
     * 查询健康数据
     * @param dto
     * @return
     */
    @Override
    public ResponseListVo queryDevicePropertyStatus(DeviceDto dto) {
        //逻辑分析
        //直接调用deviceDataService层查询相关的设备数据,返回vo集合
        List<DeviceData> deviceDataList = deviceDataService.list(Wrappers.<DeviceData>lambdaQuery().eq(DeviceData::getDeviceName, dto.getDeviceName()).eq(DeviceData::getProductKey, dto.getProductKey()));
        //创建PropertyStatusInfoVO集合,遍历list,取出所有的设备数据,封装数据
        List<PropertyStatusInfoVO> targetList = new ArrayList<>();
        deviceDataList.forEach(deviceData -> {
            PropertyStatusInfoVO propertyStatusInfoVO = new PropertyStatusInfoVO();
            propertyStatusInfoVO.setTime(String.valueOf(deviceData.getAlarmTime().toEpochSecond(ZoneOffset.UTC)*1000));
            propertyStatusInfoVO.setDataType(NumberUtil.isDouble(deviceData.getDataValue())?"double":"int" );
            propertyStatusInfoVO.setName(deviceData.getFunctionId().equals("HeartRate")?"心率":"其他");
            propertyStatusInfoVO.setUnit("");
            propertyStatusInfoVO.setValue(deviceData.getDataValue());
            propertyStatusInfoVO.setIdentifier(deviceData.getFunctionId());
            targetList.add(propertyStatusInfoVO);
        });
        ListWrapperVo listWrapperVo = new ListWrapperVo(targetList);
        ResponseListVo responseListVo = new ResponseListVo(listWrapperVo);
        return responseListVo;
    }

    /**
     * 按天统计查询指标数据
     * @param
     * @return
     */
    @Override
    public List<DeviceDataByTimeVo> queryDeviceDataListByDay(String functionId, Long startTimeStr, Long endTimeStr, String iotId) {
        /*
        思路分析
        根据前端传进来的dto中的数据进行条件查询
         */
        //将毫秒值转换成时间
        LocalDateTime startTime = DateUtil.date(startTimeStr).toLocalDateTime();
        LocalDateTime endTime = DateUtil.date(endTimeStr).toLocalDateTime();
        //调用DeviceDataService层进行条件查询
        List<DeviceData> list = deviceDataService.list(Wrappers.<DeviceData>lambdaQuery()
                .eq(DeviceData::getFunctionId, functionId)
                .eq(DeviceData::getIotId, iotId)
                .between(DeviceData::getAlarmTime, startTime, endTime));
        //遍历集合封装返回参数
        //将LocalDateTime的时间格式转换成"HH:mm"
        List<DeviceDataByTimeVo> targetList = new ArrayList<>();
        //根据每小时对数据进行分组
        Map<Integer,List<DeviceData>> deviceDataByHour = list.stream().collect(Collectors.groupingBy(item->item.getAlarmTime().getHour()));
        //获取当前时间
        Integer currentTimeHour = startTime.getHour();
        Integer endTimeHour = endTime.getHour();
        //进行循环
        while (currentTimeHour<=endTimeHour){
            if (deviceDataByHour.containsKey(currentTimeHour)) {
                //当前小时内有数据
                List<DeviceData> deviceDataList = deviceDataByHour.get(currentTimeHour);
                //遍历deviceDataList集合对数据求平均值
                double averageValue = deviceDataList.stream().mapToDouble(deviceData -> NumberUtils.toDouble(deviceData.getDataValue())).average().orElse(0.0);
                //封装数据
                DeviceDataByTimeVo deviceDataByTimeVo = DeviceDataByTimeVo.builder()
                        .dateTime(currentTimeHour.toString())
                        .dataValue(averageValue)
                        .build();
                targetList.add(deviceDataByTimeVo);
            }else{
                //当前小时没有数据
                //封装数据
                DeviceDataByTimeVo deviceDataByTimeVo = DeviceDataByTimeVo.builder()
                        .dateTime(currentTimeHour.toString())
                        .dataValue(null)
                        .build();
                targetList.add(deviceDataByTimeVo);
            }
            //步增语句
            currentTimeHour = currentTimeHour + 1;
        }
        return targetList;
    }

    /**
     * 按周统计查询指标数据
     *
     * @param
     * @return
     */
    @Override
    public List<DeviceDataByTimeVo> queryDeviceDataListByWeek(String functionId, Long startTimeStr, Long endTimeStr, String iotId) {
        /*
        思路分析
        根据前端传进来的dto中的数据进行条件查询
        与查询每日逻辑不同的时,前端只需要七个参数,如何封装这七天的参数
         */
        //将毫秒值转换成时间
        LocalDateTime startTime = DateUtil.date(startTimeStr).toLocalDateTime();
        LocalDateTime endTime = DateUtil.date(endTimeStr).toLocalDateTime();
        //调用DeviceDataService层进行条件查询
        List<DeviceData> list = deviceDataService.list(Wrappers.<DeviceData>lambdaQuery()
                .eq(DeviceData::getFunctionId, functionId)
                .eq(DeviceData::getIotId, iotId)
                .between(DeviceData::getAlarmTime, startTime, endTime));
        //遍历集合封装返回参数
        //将LocalDateTime的时间格式转换成"HH:mm"
        List<DeviceDataByTimeVo> targetList = new ArrayList<>();
        //根据每天对数据进行分组
        Map<String,List<DeviceData>> deviceDataByWeek = list.stream().collect(Collectors.groupingBy(item->item.getAlarmTime().format(DateTimeFormatter.ofPattern("MM.dd"))));
        //获取开始时间和结束时间
        LocalDate currentDate = startTime.toLocalDate();
        LocalDate endDate = endTime.toLocalDate();
        //循环判断当前currentDate是否超过endDate
        while (!currentDate.isAfter(endDate)){
            String day = currentDate.format(DateTimeFormatter.ofPattern("MM.dd"));
            //检查当天是否有数据
            if (deviceDataByWeek.containsKey(day)) {
                //说明当天有数据
                //遍历deviceDataList集合对数据求平均值
                List<DeviceData> deviceDataList = deviceDataByWeek.get(day);
                double averageValue = deviceDataList.stream().mapToDouble(deviceData -> NumberUtils.toDouble(deviceData.getDataValue())).average().orElse(0.0);
                //封装数据
                DeviceDataByTimeVo deviceDataByTimeVo = DeviceDataByTimeVo.builder()
                        .dateTime(day)
                        .dataValue(averageValue)
                        .build();
                targetList.add(deviceDataByTimeVo);
            }else{
                //说明当天没有数据
                //封装数据
                DeviceDataByTimeVo deviceDataByTimeVo = DeviceDataByTimeVo.builder()
                        .dateTime(day)
                        .dataValue(null)
                        .build();
                targetList.add(deviceDataByTimeVo);
            }
            //步增语句
            currentDate = currentDate.plusDays(1);
        }
        return targetList;
    }

    /**
     * 根据id解绑家人
     * @param id
     */
    @Override
    public void deleteById(Long id) {
        try {
            familyMemberElderMapper.deleteById(id);
        } catch (Exception e) {
            throw new BaseException("解绑失败");
        }
    }
}
